/******************************************************************************
 * \brief	队列（FIFO、Queue、缓存）的实现
 * \details	不能单独使用，不包含实际的缓存数据，需要和上层结构体结合在一起使用；
 *			既适用于操作系统中，也适用于嵌入式裸机中
 * \remarks Linux原生的队列kfifo一次只能操作一个队列，操作变长元素时，判断队列里
 *			元素个数不容易，在复杂场景下使用不方便；消息队列msgbuf的实现又太复杂，
 *			所以在这里自行实现FIFO队列（queue）模块
 * \note	File format: UTF-8，中文编码：UTF-8
 * \author	将狼才鲸
 * \date	2023-03-08
 ******************************************************************************/

#ifndef _QUEUE_H_
#define _QUEUE_H_

/*********************************** 头文件 ***********************************/
#include "util_errno.h"
#include "list.h"
#include <stddef.h>	/* NULL */

/*********************************** 宏定义 ***********************************/
#if 0//defined(TARGET_LINUX32) || defined(TARGET_LINUX64)	\
	|| defined(_MSC_VER) || defined(WIN32) || defined(_WIN64)
	/* 如果在操作系统之中，需要上锁 */
#	include <pthread.h>
	typedef pthread_mutex_t queue_lock_t;
#	define queue_lock_init(lock)	pthread_mutex_init((lock), NULL)
#	define queue_lock(lock)			pthread_mutex_lock((lock))
#	define queue_unlock(lock)		pthread_mutex_unlock((lock))
#	define queue_lock_destroy(lock)	pthread_mutex_destroy((lock))
#else	/* OS */
	/* 如果是裸机，无需上锁，定义为空 */
	typedef int queue_lock_t;
#	define queue_lock_init(lock)
#	define queue_lock(lock)
#	define queue_unlock(lock)
#	define queue_lock_destroy(lock)
#endif	/* OS */

/********************************** 类型定义 **********************************/
/* 队列中的链表类型 */
enum {
	QUEUE_USED,	/* 已使用的队列 */
	QUEUE_FREE,	/* 已释放的队列 */
	QUEUE_MEMBER_MAX
};

/* 一个队列的入口 */
typedef struct _queue_entry {
	struct list_head lists[QUEUE_MEMBER_MAX];	/* 队列中的已使用链表和已释放链表 */
	queue_lock_t lock;
} queue_entry_t;

/* 一个队列节点 */
typedef struct _queue {
	struct list_head node;	/* 队列里的一个缓存对应的节点 */
	/* 该节点所属的队列（因为可能会同时存在多个队列，节点也可以跨队列拷贝，
	   所以每个队列元素自身携带队列入口） */
	queue_entry_t *queue_entry;
} queue_t;

/*
 * 队列使用场景：
 *		a）缓存的申请和释放；
 *		b）设备间通信、核间通信时，对发送和接收的包进行缓存；
 *		c）模块间通信时，每个模块都维护一个属于自己的队列；
 * 可以多个队列同时存在：例如SRAM缓存、DDR缓存、通信线路、每个模块都
 *		维护自己的队列；
 * 实现原理：
 *			队列头（入口）有个自己的结构体，每个队列都需要定义此变量；
 *		每条队列中维护2个链表：一个已释放链表、一个已使用链表，
 *		如果是元素动态申请和释放的队列，使用其中的‘已使用链表’即可，
 *		如果是预先静态分配好队列容量，则两个链表都使用；
 *		在操作系统中可以动态分配，在单片机裸机中需要静态分配；
 *			每个队列元素中除了携带链表节点外，还携带有自己队列入口的指针，
 *		这样可以在不同队列间交换元素
 */

/********************************** 接口函数 **********************************/
/**
 * \brief	初始化队列入口
 * \param	pqe:	使用队列时定义好的队列入口
 */
extern void queue_entry_init(queue_entry_t *pqe);

/**
 * \brief	销毁队列
 * \param	pqe:	queue_entry_t * 队列入口
 */
#define queue_entry_destroy(pqe) queue_lock_destroy((pqe)->lock)

/**
 * \brief	初始化一个队列中的元素
 */
extern void queue_init(queue_t *pq);

/**
 * \brief	将队列元素推入到其它队列，或推入到自己队列的已使用链表中或已释放链表中
 * \param	pqe:	为空时表示推入到自己元素所在的队列，非空代表要推入的队列（跨队列操作）
 * \param	pq:		要推入的队列元素
 * \param	line:	推入到哪个链表：已使用链表或已释放链表，QUEUE_USED or QUEUE_FREE
 */
extern int queue_put(queue_entry_t *pqe, queue_t *pq, int line);

/**
 * \brief	从队列入口的已使用链表或已释放链表中获取一个队列元素
 * \param	line:	哪个链表：已使用链表或已释放链表，QUEUE_USED or QUEUE_FREE
 * \return	获取到的队列节点；获取失败则返回NULL
 */
extern queue_t *queue_get(queue_entry_t *pqe, int line);

/**
 * \brief	从队列中申请一个可用的缓存
 * \details	只在已静态初始化的队列中使用，如果是动态申请和释放的队列元素，
 *			则不会用到QUEUE_FREE这个链表
 * \param	pge:	queue_entry_t * 队列入口指针
 * \return	queue_t *:	获取的队列节点；获取失败则返回NULL
 */
#define queue_alloc(pqe)	queue_get((pqe), QUEUE_FREE)

/**
 * \brief	用完后将缓存进行释放
 * \details	只在已静态初始化的队列中使用，如果是动态申请和释放的队列元素，
 *			则不会用到QUEUE_FREE这个链表
 * \param	pg:	queue_t * 一个缓存节点指针
 * \return	错误码
 */
#define queue_free(pq)	queue_put(NULL, (pq), QUEUE_FREE)

/**
 * \brief	预览已使用或者已释放链表中的第一个元素
 * \details	该元素预览后不会被弹出
 * \param	line:	QUEUE_USED or QUEUE_FREE
 */
extern queue_t *queue_peek(queue_entry_t *pqe, int line);

/**
 * \brief	获取已使用或者已释放链表中剩余的元素个数
 * \param	line:	QUEUE_USED or QUEUE_FREE
 */
extern int queue_count(queue_entry_t *pqe, int line);

#endif /* _QUEUE_H_ */

/*********************************** 文件尾 ***********************************/
